/******************************************************************/
/*                                                                */
/*  Winpooch : Windows Watchdog                                   */
/*  Copyright (C) 2004  Benoit Blanchon                           */
/*                                                                */
/*  This program is free software; you can redistribute it        */
/*  and/or modify it under the terms of the GNU General Public    */
/*  License as published by the Free Software Foundation; either  */
/*  version 2 of the License, or (at your option) any later       */
/*  version.                                                      */
/*                                                                */
/*  This program is distributed in the hope that it will be       */
/*  useful, but WITHOUT ANY WARRANTY; without even the implied    */
/*  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR       */
/*  PURPOSE.  See the GNU General Public License for more         */
/*  details.                                                      */
/*                                                                */
/*  You should have received a copy of the GNU General Public     */
/*  License along with this program; if not, write to the Free    */
/*  Software Foundation, Inc.,                                    */
/*  675 Mass Ave, Cambridge, MA 02139, USA.                       */
/*                                                                */
/******************************************************************/

// module's interface
#include "RegWatcher.h"

// standard headers
#include <assert.h>
#include <tchar.h>

// project's headers
#include "Language.h"
#include "Log.h"
#include "MainWindow.h"

#define MAX_MESSAGE_LEN		256

// real structure behind HREGWATCHER
typedef struct {
  HWND		hwnd ;
  HANDLE	hThread ;
  TCHAR		szKey[MAX_PATH] ;
  BOOL		bSubTree ; 
  HANDLE	hStopEvent ;
} REGWATCHER ;

// filter used in RegNotifyChangeKeyValue
static DWORD dwFilter = REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET ;

// internal functions
DWORD WINAPI _RegWatcher_Thread (LPVOID pContext)
{
  REGWATCHER   *p = (REGWATCHER*) pContext ;

  TCHAR		szMessage[MAX_MESSAGE_LEN] ;
  BOOL		bGoOn = TRUE ;
  HANDLE	aEvents[2] ;
  HKEY		hkey, hkeyRoot ;
  HANDLE	hNotifyEvent ;
  LPCTSTR	szSubKey ;

  assert (p!=NULL) ;
  
  // select root key
  if( ! _tcsncmp(p->szKey,TEXT("HKCR\\"), 5) ) {
    hkeyRoot = HKEY_CLASSES_ROOT ;
    szSubKey = p->szKey + 5 ;
  } 
  else if( ! _tcsncmp(p->szKey,TEXT("HKCU\\"), 5) ) {
    hkeyRoot = HKEY_CURRENT_USER ;
    szSubKey = p->szKey + 5 ;    
  }
  else if( ! _tcsncmp(p->szKey,TEXT("HKLM\\"), 5) ) {
    hkeyRoot = HKEY_LOCAL_MACHINE ;
    szSubKey = p->szKey + 5 ;
  }
  else if( ! _tcsncmp(p->szKey,TEXT("HKU\\"), 4) ) {
    hkeyRoot = HKEY_USERS ;
    szSubKey = p->szKey + 4 ; 
  }
  else if( ! _tcsncmp(p->szKey,TEXT("HKCC\\"), 5) ) {
    hkeyRoot = HKEY_CURRENT_CONFIG ;
    szSubKey = p->szKey + 5 ; 
  }
  else {
    MainWnd_SendAlertf (p->hwnd, ALERT_ERROR, 
			STR(_INVALID_ROOT_KEY_OF_S),
			p->szKey) ;
    return 1 ;
  }


  // open a handle to the specified key
  if( ERROR_SUCCESS!=RegOpenKeyEx (hkeyRoot, szSubKey, 0, 
				   KEY_NOTIFY, &hkey) )
    {
      MainWnd_SendAlertf (p->hwnd, ALERT_ERROR, 
			  STR(_INVALID_KEY_S),
			  p->szKey) ;
      return 2 ;
    }

  // Log
  Log_Printf (STR(_START_WATCHING_KEY_S), p->szKey) ;

  // create the event used by RegNotifyChangeKeyValue
  hNotifyEvent = CreateEvent (NULL, FALSE, FALSE, NULL) ;
    
  // store events in a table, to use WaitForMultipleObjects
  aEvents[0] = hNotifyEvent ;
  aEvents[1] = p->hStopEvent ;    
  
  while( bGoOn )
    {
      RegNotifyChangeKeyValue (hkey, TRUE, dwFilter,
			       hNotifyEvent, TRUE) ;

      switch( WaitForMultipleObjects (2, aEvents, FALSE, INFINITE) )
	{
	case WAIT_OBJECT_0:
	  
	  // format message
	  wsprintf (szMessage, STR(_KEY_S_MODIFIED), p->szKey) ;

	  Log_Print (szMessage) ;
	  
	  MainWnd_SendAlert (p->hwnd, ALERT_DETECTION, szMessage) ;

	  break ;
	  
	default:
	  bGoOn = FALSE ;	  
	} 
    }

  // Log
  Log_Printf (STR(_STOP_WATCHING_KEY_S), p->szKey) ;

  // Close handles
  CloseHandle (hNotifyEvent) ;
  RegCloseKey (hkey) ;
  
  return 0 ;
}




HREGWATCHER RegWatcher_New (HWND hwnd, LPCTSTR szKey, BOOL bSubTree)
{
  REGWATCHER * p ;
  DWORD	dwThreadId ;
  
  p = malloc (sizeof(REGWATCHER)) ;

  p->hwnd = hwnd ;
  p->bSubTree = bSubTree ;
  p->hStopEvent = CreateEvent (NULL, FALSE, FALSE, NULL) ;
  lstrcpy (p->szKey, szKey) ;  

  p->hThread = CreateThread (NULL, 0, _RegWatcher_Thread, p, 0, &dwThreadId) ;
  
  return p ;
}

VOID RegWatcher_Delete (HREGWATCHER h)
{
  REGWATCHER * p = (REGWATCHER*)h ;

  if( p ) 
    {
      SetEvent (p->hStopEvent) ;

      WaitForSingleObject (p->hThread, 10000) ;

      CloseHandle (p->hThread) ;
      CloseHandle (p->hStopEvent) ;

      free (p) ;
    }
}
